/*数据管理模块
    1.文件的实际存储路径
        当客户端要下载文件时，则从这个文件中读取数据进行响应
    2.文件压缩包存放路径名
        如果这个文件是一个非热点文件，则会被压缩，这个就是压缩包路径名称
        如果客户端要下载这个文件，则需要先解压缩，再读解压后的文件数据
    3.文件是否被压缩的标志位
        判断文件是否被压缩了
    4.文件大小
    5.文件最后一次修改时间
    6.文件最后一次被访问的时间
    7.文件访问URL中资源路径
        下载连接资源path：/download/a.txt

*/

/*管理数据
    1.用于数据信息访问
        使用哈希表在内存中管理数据，以url作为key查询内部存储的数据-查询速度快
    2.持久化存储管理
        使用json序列化将所有数据信息保存在文件中。
*/

#pragma once

#include <iostream>
#include <ctime>
#include <string>
#include <unordered_map>
#include <vector>
#include <pthread.h>

#include "util.hpp"
#include "config.hpp"

namespace cloud
{
    // 数据信息结构体
    typedef struct BackupInfo_t
    {
        bool NewBackupInfo(const std::string &realpath) // 通过一个路径，调用函数填充结构体
        {
            FileUtil fu(realpath);
            if (fu.Exists() == false)
            {
                std::cout << "new backupinfo: file not exists" << std::endl;
                return false;
            }
            Config *config = Config::GetInstance();
            std::string packdir = config->GetPackDir();
            std::string packsuffix = config->GetPackFileSuffix();
            std::string download_prefix = config->GetDownloadPrefix();
            pack_flag = false;
            fsize = fu.FileSize();
            atime = fu.LastATime();
            mtime = fu.LastMTime();
            real_path = realpath;
            pack_path = packdir + fu.FileName() + packsuffix; // 压缩文件存储路径 + 文件名 + 压缩文件的后缀名
            url = download_prefix + fu.FileName();            // 文件下载前缀路径 + 文件名

            return true;
        }

        bool pack_flag; // 是否压缩标志
        size_t fsize;
        time_t atime;
        time_t mtime;
        std::string real_path; // 文件实际存储路径
        std::string pack_path; // 压缩包存储路径
        std::string url;       // URL资源路径
    } BackupInfo;

    // 数据管理类
    class DataManager
    {
    private:
        std::string _backup_file;                           // 持久化存储文件
        std::unordered_map<std::string, BackupInfo> _table; // 哈希表
        pthread_rwlock_t _rwlock;                           // 读写锁--读共享，写互斥。
    public:
        DataManager()
        {
            _backup_file = Config::GetInstance()->GetBackupFile();
            pthread_rwlock_init(&_rwlock, nullptr); // 初始化读写锁
            InitLoad();
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock); // 销毁读写锁
        }
        bool Storage() // 每次数据新增或修改都要重新持久化存储，避免数据丢失
        {
            //1.获取所有数据
            std::vector<BackupInfo> arry;
            GetAll(&arry);
            //2.添加到Json::Value 
            Json::Value root;
            for(int i = 0; i < arry.size(); i++)
            {
                Json::Value item;
                item["pack_flag"] = arry[i].pack_flag;
                item["fsize"] = static_cast<int>(arry[i].fsize);  //json的operator=只重载了基础类型，对于size_t,time_t是没有重载的
                item["atime"] = static_cast<int>(arry[i].atime);
                item["mtime"] = static_cast<int>(arry[i].mtime);
                item["real_path"] = arry[i].real_path;
                item["pack_path"] = arry[i].pack_path;
                item["url"] = arry[i].url;
                root.append(item); //添加数组元素
            }
            //3.对Json::Value序列化
            std::string body;
            JsonUtil::Serialize(root, &body);
            //4.写文件
            FileUtil fu(_backup_file);
            fu.SetContent(body);

            return true;
        }
        bool InitLoad() // 初始化加载，在每次系统重起都要加载以前的数据
        {
            //1.将文件中的数据读取出来
            FileUtil fu(_backup_file);
            if(!fu.Exists())
                return true;
            std::string body;
            fu.GetContent(&body);
            
            //2.反序列化
            Json::Value root;
            JsonUtil::Unserialize(body, &root);
            //3.将反序列化得到的json::Value数据添加到table中
            for(int i = 0; i < root.size(); i++)
            {
                BackupInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = root[i]["fsize"].asInt64();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.real_path = root[i]["real_path"].asString();
                info.pack_path = root[i]["pack_path"].asString();
                info.url = root[i]["url"].asString();
                Insert(info);
            }

            return true;
        }

        bool Insert(const BackupInfo &info) // 新增
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();   //新增数据时，存储
            return true;
        }

        bool Updata(const BackupInfo &info) // 修改
        {
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info;
            pthread_rwlock_unlock(&_rwlock);
            Storage();  //修改数据时，存储
            return true;
        }

        bool GetOneByUrl(const std::string &url, BackupInfo *info) // 根据url获取资源
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(url);
            if (it == _table.end())
            {
                pthread_rwlock_unlock(&_rwlock);
                return false;
            }
            *info = it->second;
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        bool GetOneByRealpath(const std::string &realpath, BackupInfo *info) // 遍历目录，查找文件
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); ++it)
            {
                if (it->second.real_path == realpath)
                {
                    *info = it->second;
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }

        bool GetAll(std::vector<BackupInfo> *arry)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); ++it)
            {
                arry->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
    };
}